package net.callumtaylor.gridlistview;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.HeaderViewListAdapter;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import java.util.ArrayList;
public class GridListView extends ListView
{
private int mNumColumns = 0;
private OnItemClickListener clickListener;
private OnItemLongClickListener longClickListener;
public GridListView(Context context)
{
super(context);
}
public GridListView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public GridListView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.GridListView);
mNumColumns = attributes.getInt(R.styleable.GridListView_numColumns, 1);
attributes.recycle();
setSelector(new ColorDrawable(0x00000000));
}
public void setNumColumns(int columnCount)
{
this.mNumColumns = columnCount;
if (getAdapter() != null)
{
((InternalAdapterImpl)getAdapter()).notifyDataSetChanged();
}
}
public int getNumColumns()
{
return this.mNumColumns;
}
@Override public void setAdapter(ListAdapter adapter)
{
if (!(adapter instanceof BaseAdapter))
{
super.setAdapter(adapter);
return;
}
InternalAdapterImpl wrapper = new InternalAdapterImpl(getContext(), (BaseAdapter)adapter);
wrapper.generateColumnSpec();
super.setAdapter(wrapper);
}
/**
* You must call this instead of {@link android.widget.ListView#getAdapter()} to get
* your base adapter. Calling {@link android.widget.ListView#getAdapter()} will return the wrapper
* adapter for the grid view.
* @return
*/
public ListAdapter getBaseAdapter()
{
ListAdapter adapter = super.getAdapter();
if (adapter instanceof HeaderViewListAdapter)
{
adapter = (InternalAdapterImpl)((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
if (adapter != null)
{
return ((InternalAdapterImpl)adapter).baseAdapter;
}
return null;
}
@Override public void setOnItemClickListener(OnItemClickListener listener)
{
this.clickListener = listener;
}
@Override public void setOnItemLongClickListener(OnItemLongClickListener listener)
{
this.longClickListener = listener;
}
@Override public boolean performItemClick(View view, int position, long id)
{
if (this.clickListener != null)
{
this.clickListener.onItemClick(this, view, position, id);
return true;
}
return false;
}
private class InternalAdapterImpl extends BaseAdapter
{
private BaseAdapter baseAdapter;
private Context context;
private Integer[] columnSpec;
public InternalAdapterImpl(Context context, BaseAdapter adapter)
{
this.baseAdapter = adapter;
this.context = context;
}
private void generateColumnSpec()
{
if (baseAdapter instanceof GridAdapter)
{
ArrayList<Integer> columnSpecList = new ArrayList<Integer>();
int total = getBaseCount();
int index = 0;
while (total > 0)
{
int columnCount = ((GridAdapter)baseAdapter).getColumnCount(index);
columnSpecList.add(columnCount < 1 ? mNumColumns : columnCount);
total -= columnSpecList.get(index++);
}
columnSpec = columnSpecList.toArray(new Integer[columnSpecList.size()]);
}
}
private int getBaseCount()
{
return baseAdapter.getCount();
}
@Override public int getCount()
{
return columnSpec == null ? (int)Math.ceil((float)baseAdapter.getCount() / (float)getNumColumns()) : columnSpec.length;
}
@Override public Object getItem(int position)
{
return baseAdapter.getItem(position);
}
@Override public long getItemId(int position)
{
return baseAdapter.getItemId(position);
}
@Override public boolean hasStableIds()
{
return baseAdapter.hasStableIds();
}
@Override public void registerDataSetObserver(DataSetObserver observer)
{
baseAdapter.registerDataSetObserver(observer);
}
@Override public void unregisterDataSetObserver(DataSetObserver observer)
{
baseAdapter.unregisterDataSetObserver(observer);
}
@Override public void notifyDataSetChanged()
{
generateColumnSpec();
super.notifyDataSetChanged();
baseAdapter.notifyDataSetChanged();
}
@Override public void notifyDataSetInvalidated()
{
super.notifyDataSetInvalidated();
baseAdapter.notifyDataSetInvalidated();
}
@Override public boolean areAllItemsEnabled()
{
return baseAdapter.areAllItemsEnabled();
}
@Override public boolean isEnabled(int position)
{
return baseAdapter.isEnabled(position);
}
@Override public View getDropDownView(int position, View convertView, ViewGroup parent)
{
return baseAdapter.getDropDownView(position, convertView, parent);
}
@Override public int getItemViewType(int position)
{
return baseAdapter.getItemViewType(position);
}
@Override public int getViewTypeCount()
{
return baseAdapter.getViewTypeCount();
}
@Override public boolean isEmpty()
{
return baseAdapter.isEmpty();
}
@Override public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(context).inflate(R.layout.row, parent, false);
}
int columnCount = mNumColumns;
int previousItems = 0;
if (baseAdapter instanceof GridAdapter)
{
columnCount = columnSpec[position];
columnCount = columnCount < 1 ? mNumColumns : columnCount;
for (int index = 0; index < position; index++)
{
previousItems += columnSpec[index];
}
}
else
{
previousItems += mNumColumns * position;
}
((LinearLayout)convertView).setWeightSum(columnCount);
View[] convertViews = new View[columnCount];
for (int index = 0; index < columnCount; index++)
{
if (previousItems + index >= getBaseCount()) break;
GridCellWrapper wrapper;
if ((wrapper = (GridCellWrapper)((LinearLayout)convertView).getChildAt(index)) == null)
{
wrapper = new GridCellWrapper(context);
}
View v = baseAdapter.getView(previousItems + index, wrapper.getChildAt(0), wrapper);
wrapper.removeAllViews();
wrapper.addView(v);
convertViews[index] = wrapper;
}
((LinearLayout)convertView).removeAllViews();
for (int index = 0; index < columnCount; index++)
{
if (convertViews[index] == null) break;
final int pos = previousItems + index;
if (baseAdapter.isEnabled(pos))
{
convertViews[index].setOnClickListener(new OnClickListener()
{
@Override public void onClick(View v)
{
if (clickListener != null)
{
clickListener.onItemClick(GridListView.this, v, pos, baseAdapter.getItemId(pos));
}
}
});
if (longClickListener != null)
{
convertViews[index].setOnLongClickListener(new OnLongClickListener()
{
@Override public boolean onLongClick(View v)
{
return longClickListener.onItemLongClick(GridListView.this, v, pos, baseAdapter.getItemId(pos));
}
});
}
}
((LinearLayout)convertView).addView(convertViews[index]);
}
return convertView;
}
}
}